# Copyright (C) 2005-2006 Frederic Back <fredericback@gmail.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
# for more details.

"""
Basic loading and saving functionality for projects.
"""

from xml.sax import ContentHandler, make_parser
from xml.sax.saxutils import XMLGenerator
from xml.sax.xmlreader import AttributesImpl

import codecs

import gtk
from _guicore import PGButler
from _geodata import ResultSet, Layer, Project

class ProjectExtension:
    """ Load and Save functionality. 
        Has been separated from the main GUI and is now an extension. """

    def __init__(self):
        # menu items to add
        ui = """<ui>
            <menubar name="MenuBar">
                <menu action="Project">
                    <placeholder name="LoadAndSave">
                        <menuitem action="New"/>
                        <menuitem action="Open"/>
                        <separator />
                        <menuitem action="Save"/>
                        <menuitem action="SaveAs"/>
                    </placeholder>
                </menu>
            </menubar>
         </ui>"""

        # strings for translation
        self.i18n = {
            "NewProject" : _('_New Project'),
            "OpenProject" : _('_Open Project'),
            "SaveProject" : _('_Save Project'),
            "SaveProjectAs" : _('Save Project _As...'),
        }

        # register actions
        actiongroup = gtk.ActionGroup("ProjectExtensionActions")
        actiongroup.add_actions([
            ('New', gtk.STOCK_NEW, self.i18n["NewProject"], None, self.i18n["NewProject"].replace("_",""), self.cb_new),
            ('Open', gtk.STOCK_OPEN, self.i18n["OpenProject"], None, self.i18n["OpenProject"].replace("_",""), self.cb_load),
            ('Save', gtk.STOCK_SAVE, self.i18n["SaveProject"], None, self.i18n["SaveProject"].replace("_",""), self.cb_save),
            ('SaveAs', gtk.STOCK_SAVE_AS, self.i18n["SaveProjectAs"], None, self.i18n["SaveProjectAs"].replace("_",""), self.cb_saveAs),
        ])
         
        # load ui into PGButler
        PGButler.instance.uimanager.insert_action_group(actiongroup, 0)
        PGButler.instance.uimanager.add_ui_from_string(ui)
        
        # create a new document
        self.cb_new()
        
        
    def cb_new(self, w=None):
        PGButler.instance.layerView.setProject( Project() )

    def cb_save(self, w=None):
        if PGButler.instance.layerView.getProject().filename is None: return self.cb_saveAs() 
        #handle = sys.stdout
        handle = open(PGButler.instance.layerView.getProject().filename, 'w')
        s = Saver(handle,PGButler.instance.layerView.getProject())
        handle.close()
        

    def createProjectFileChooser(self, action):
        """ subject to change... """

        if action == "Open":
            titlestring = self.i18n["OpenProject"].replace("_","")
            gtkaction = gtk.FILE_CHOOSER_ACTION_OPEN
        elif action == "Save":
            titlestring = self.i18n["SaveProject"].replace("_","")
            gtkaction = gtk.FILE_CHOOSER_ACTION_SAVE
        else: raise ValueError

        f = gtk.FileChooserDialog(titlestring,
            PGButler.instance.win,gtkaction,(gtk.STOCK_CANCEL,0,gtk.STOCK_OK,1))
        f.set_current_name( "new.mezo" )
        filefilter = gtk.FileFilter()
        filefilter.set_name(_("mezoGIS project files"))
        filefilter.add_pattern("*.mezo")
        f.add_filter(filefilter)
        filefilter = gtk.FileFilter()
        filefilter.set_name(_("all files"))
        filefilter.add_pattern("*")
        f.add_filter(filefilter)
        return f


    def cb_saveAs(self, w=None):
        f = self.createProjectFileChooser("Save")
        f.show()
        r = f.run()
        f.hide()
        if r == 1:
            PGButler.instance.layerView.getProject().filename = f.get_filename()
            if PGButler.instance.layerView.getProject().filename != None:
                self.cb_save()
        f.destroy()
        

    def cb_load(self, w=None):
        f = self.createProjectFileChooser("Open")
        f.show()
        r = f.run()
        f.hide()

        if r == 1:
            result = None
            results = []
            layer = None

            try:
                self.cb_new()
                PGButler.instance.layerView.getProject().filename = f.get_filename()
                handle = open(PGButler.instance.layerView.getProject().filename,"r")
                loader = Loader(handle,PGButler.instance.layerView.getProject())
                handle.close()  

            except Exception, e:
                self.cb_new() # clear
                d = gtk.MessageDialog(PGButler.instance.win.get_toplevel(),
                    gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
                    gtk.MESSAGE_ERROR, gtk.BUTTONS_OK,
                    _("An error occurred while loading the project:") )
                d.format_secondary_text(str(e))
                d.run()
                d.destroy()

        f.destroy()
        
PGButler.instance.register_extension( ProjectExtension )        


class Saver:

    def __init__(self, filehandle, project):
        """
        filehandle: A file handle as returned by open('my.mezo', 'w')
        resultsets: A list of ResultSet instances to be serialized
        """

        self.filehandle = filehandle
        f = filehandle
        f.write('<?xml version="1.0" encoding="iso-8859-1"?>\n')
        f.write('<project>\n')

        for result in project.resultsets:
            f.write('<result name="%s"'%self.encode(result.name))
            if result.viewExtent is not None:
                f.write(' extent="%s,%s,%s,%s"'%result.viewExtent)
            f.write('>\n')

            for layer in result.layers:
                f.write('\t<layer ')
                data = self.getLayerAttributes(layer)
                for row in data:
                    f.write('%s="%s" '%(row,self.encode(data[row])))
                       
                f.write(' />\n')
            f.write('</result>\n')
        f.write('</project>\n')

    def encode(self, string):
        a = unicode(str(string))
        a = a.encode('ascii', 'xmlcharrefreplace')
        # would be cleaner to use codec translation stuff, but oh well
        a = a.replace('"', "&#34;")
        a = a.replace("'", "&#39;")
        a = a.replace(">", "&gt;")  
        a = a.replace("<", "&lt;")
        a = a.replace("&", "&amp;")
        return a
        
    def getLayerAttributes(self,layer):
        d = {}
        d["name"] = layer.name
        d["query"] = layer.query
        d["geometry"] = layer.geometryType
        d["outline"] = layer.style.outline
        d["line"] = layer.style.line
        d["fill"] = layer.style.fill
        d["linewidth"] = layer.style.linewidth
        d["symbol"] = layer.style.symbol
        d["symbolsize"] = layer.style.symbolsize
        
        d["labelFlag"] = layer.style.labelFlag
        d["labelcolumn"] = layer.style.labelcolumn
        d["labelsyntax"] = layer.style.labelsyntax
        d["labelfont"] = layer.style.labelfont
        d["labelcolour"] = layer.style.labelcolour                     
        return d

class Loader(ContentHandler):

    def __init__(self, filehandle, project):
        self.project = None
        self.result = None
        self.project = project
        saxparser = make_parser()
        saxparser.setContentHandler(self)
        saxparser.parse(filehandle)

    def setLayerAttribute(self, layer, attribute, data):
        if attribute == "name": layer.name = self.decode(data)
        elif attribute == "query": layer.query = self.decode(data)
        elif attribute == "geometry": layer.geometryType = self.decode(data)
        elif attribute == "outline": layer.style.outline.fromString(data)
        elif attribute == "fill": layer.style.fill.fromString(data)
        elif attribute == "line": layer.style.line.fromString(data)
        elif attribute == "linewidth": layer.style.linewidth = float(data)
        elif attribute == "symbol": layer.style.symbol = self.decode(data)
        elif attribute == "symbolsize": layer.style.symbolsize = float(data)
        
        elif attribute == "labelFlag": layer.style.labelFlag = bool(data)
        elif attribute == "labelcolumn": layer.style.labelcolumn = int(data)
        elif attribute == "labelsyntax": layer.style.labelsyntax = str(data)
        elif attribute == "labelfont": layer.style.labelfont = str(data)
        elif attribute == "labelcolour": layer.style.labelcolour.fromString(data)
        
        else: print "Skip unknown attribute '%s'."%attribute

    def setResultAttribute(self, result, attribute, data):
        if attribute == "name": result.name = self.decode(data)
        elif attribute == "extent":
            try:
                e = data.split(",")
                result.viewExtent = (float(e[0]),float(e[1]),float(e[2]),float(e[3]))
            except:
                result.viewExtent = None
        else: print "Skip unknown attribute '%s'."%attribute

    def decode(self, a):
        a = str(a)
        a = a.replace("&#34;",'"')
        a = a.replace("&#39;","'")
        a = a.replace("&gt;",">")  
        a = a.replace("&lt;","<")
        a = a.replace("&amp;","&")
        return a

    def startElement(self, name, attrs):
        if name == "result":
            self.result = ResultSet()
            for a in attrs.getNames():
                self.setResultAttribute(self.result, a, attrs.getValue(a))
            self.project.addResultset(self.result)

        if name == "layer":
            layer = Layer()
            for a in attrs.getNames():
                self.setLayerAttribute(layer, a, attrs.getValue(a))
            if self.result: self.result.addLayer(layer)

    def endElement(self, name):
        pass

    def characters(self, chrs):
        pass

    def startDocument(self):
        pass

    def endDocument(self):
        pass
